home *** CD-ROM | disk | FTP | other *** search
/ Atari Mega Archive 1 / Atari Mega Archive - Volume 1.iso / gnu / bash / bash_108 / bash-108.zoo / bash-1.08 / fc.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-05-19  |  14.2 KB  |  633 lines

  1. /* fc.c -- the K*rn shell style `fc' builtin. */
  2.  
  3. /* Copyright (C) 1987,1991 Free Software Foundation, Inc.
  4.  
  5.    This file is part of GNU Bash, the Bourne Again SHell.
  6.  
  7.    Bash is free software; you can redistribute it and/or modify it
  8.    under the terms of the GNU General Public License as published by
  9.    the Free Software Foundation; either version 1, or (at your option)
  10.    any later version.
  11.  
  12.    Bash is distributed in the hope that it will be useful, but WITHOUT
  13.    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  14.    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
  15.    License for more details.
  16.  
  17.    You should have received a copy of the GNU General Public License
  18.    along with Bash; see the file COPYING.  If not, write to the Free
  19.    Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
  20.  
  21. #include <stdio.h>
  22. #include <sys/param.h>
  23. #include "shell.h"
  24. #include "builtins.h"
  25. #include <readline/history.h>
  26.  
  27. #if defined (NULL)
  28. #undef NULL
  29. #endif
  30. #define NULL 0
  31.  
  32. /* **************************************************************** */
  33. /*                                    */
  34. /*    The K*rn shell style fc command (Fix Command)            */
  35. /*                                    */
  36. /* **************************************************************** */
  37.  
  38. /* fc builtin command (fix command) for Bash for those who
  39.    like K*rn-style history better than csh-style.
  40.  
  41.      fc [-e ename] [-nlr] [first] [last]
  42.  
  43.    FIRST and LAST can be numbers specifying the range, or FIRST can be
  44.    a string, which means the most recent command beginning with that
  45.    string.
  46.  
  47.    -e ENAME selects which editor to use.  Default is FCEDIT, then EDITOR,
  48.       then readline editing mode, then vi.
  49.  
  50.    -l means list lines instead of editing.
  51.    -n means no line numbers listed.
  52.    -r means reverse the order of the lines (making it newest listed first).
  53.  
  54.      fc -e - [pat=rep ...] [command]
  55.      fc -s [pat=rep ...] [command]
  56.  
  57.    Equivalent to !command:sg/pat/rep execpt there can be multiple PAT=REP's.
  58. */
  59.  
  60. static char *fc_dosubs (), *fc_replace (), *fc_gethist (), *fc_readline ();
  61. static int fc_gethnum ();
  62. static void fc_replhist (), fc_addhist ();
  63.  
  64. /* Data structure describing a list of global replacements to perform. */
  65. typedef struct repl {
  66.   struct repl *next;
  67.   char *pat;
  68.   char *rep;
  69. } REPL;
  70.  
  71. #define USAGE    "usage: fc [-e ename] [-nlr] [first] [last] or fc -s [pat=rep] [command]"
  72.  
  73. /* True if A is the first substring in B. */
  74. #define prefix(a, b) (strncmp ((a), (b), strlen ((a))) == 0)
  75.  
  76. /* Accessors for HIST_ENTRY lists that are called HLIST. */
  77. #define histline(i) (hlist[(i)]->line)
  78. #define histdata(i) (hlist[(i)]->data)
  79.  
  80. #define FREE_RLIST() \
  81.     do { \
  82.         for (rl = rlist; rl; ) { \
  83.             REPL *r;    \
  84. \
  85.             r = rl->next; \
  86.             if (rl->pat) \
  87.                 free (rl->pat); \
  88.             if (rl->rep) \
  89.                 free (rl->rep); \
  90.             free (rl); \
  91.             rl = r; \
  92.         } \
  93.     } while (0)
  94.  
  95. /* String to execute on a file that we want to edit. */
  96. #define FC_EDIT_COMMAND "${FCEDIT:-${EDITOR:-vi}}"
  97.  
  98. int
  99. fc_builtin (list)
  100.      WORD_LIST *list;
  101. {
  102.   register int i;
  103.   register char *sep;
  104.   int numbering, reverse, listing, execute;
  105.   int histbeg, histend, last_hist, retval, first;
  106.   FILE *stream;
  107.   REPL *rlist = (REPL *) NULL, *rl;
  108.   char *ename = NULL, *command, *newcom, *line;
  109.   HIST_ENTRY **hlist;
  110.   char fn[MAXPATHLEN];
  111.  
  112.   numbering = 1;
  113.   reverse = listing = execute = 0;
  114.  
  115.   /* Parse out the options and set which of the two forms we're in. */
  116.   while (list && *list->word->word == '-')
  117.     {
  118.       register char *s = &((list->word->word)[1]);
  119.       register int c;
  120.  
  121.       if (!isletter (*s))    /* for stuff like fc -e - -2 */
  122.     break;
  123.  
  124.       while (c = *s++)
  125.     {
  126.       switch (c)
  127.         {
  128.         case 'n':
  129.           numbering = 0;
  130.           break;
  131.  
  132.         case 'l':
  133.           listing = 1;
  134.           break;
  135.  
  136.         case 'r':
  137.           reverse = 1;
  138.           break;
  139.  
  140.         case 's':
  141.           execute = 1;
  142.           break;
  143.  
  144.         case 'e':
  145.           list = list->next;
  146.  
  147.           if (list == NULL)
  148.         {
  149.           builtin_error (USAGE);
  150.           return (EXECUTION_FAILURE);
  151.         }
  152.  
  153.           ename = list->word->word;
  154.           break;
  155.  
  156.         default:
  157.           builtin_error (USAGE);
  158.           return (EXECUTION_FAILURE);
  159.         }
  160.     }
  161.       list = list->next;
  162.     }
  163.  
  164.   if (ename && (*ename == '-') && (ename[1] == '\0'))
  165.     execute = 1;
  166.  
  167.   /* The "execute" form of the command (re-run, with possible string
  168.      substitutions). */
  169.   if (execute)
  170.     {
  171.       while (list && ((sep = (char *)index (list->word->word, '=')) != NULL))
  172.     {
  173.       *sep++ = '\0';
  174.       rl = (REPL *)xmalloc (sizeof (REPL));
  175.       rl->next = (REPL *)NULL;
  176.       rl->pat = savestring (list->word->word);
  177.       rl->rep = savestring (sep);
  178.  
  179.       if (rlist == NULL)
  180.         rlist = rl;
  181.       else
  182.         {
  183.           rl->next = rlist;
  184.           rlist = rl;
  185.         }
  186.       list = list->next;
  187.     }
  188.  
  189.       /* If we have a list of substitutions to do, then reverse it
  190.      to get the replacements in the proper order. */
  191.  
  192.       if (rlist && rlist->next)
  193.     rlist = (REPL *) reverse_list ((GENERIC_LIST *) rlist);
  194.  
  195.       hlist = history_list ();
  196.  
  197.       /* If we still have something in list, it is a command spec.
  198.      Otherwise, we use the most recent command in time. */
  199.       if (list)
  200.     command = fc_gethist (list->word->word, hlist);
  201.       else
  202.     command = fc_gethist ((char *) NULL, hlist);
  203.  
  204.       if (command == NULL)
  205.     {
  206.       builtin_error ("no command found");
  207.       if (rlist)
  208.         FREE_RLIST ();
  209.  
  210.       return (EXECUTION_FAILURE);
  211.     }
  212.  
  213.       if (rlist)
  214.     {
  215.       newcom = fc_dosubs (command, rlist);
  216.       free (command);
  217.       FREE_RLIST ();
  218.       command = newcom;
  219.     }
  220.  
  221.       printf ("%s\n", command);
  222.       fc_replhist (command);    /* replace `fc -e -' with command */
  223.       return (parse_and_execute (command, "fc"));
  224.     }
  225.  
  226.   /* This is the second form of the command (the list-or-edit-and-rerun
  227.      form). */
  228.   hlist = history_list ();
  229.   for (i = 0; hlist[i]; i++);
  230.  
  231.   /* With the Bash implementation of history, the current command line
  232.      ("fc blah..." and so on) is already part of the history list by
  233.      the time we get to this point.  This just skips over that command
  234.      and makes the last command that this deals with be the last command
  235.      the user entered before the fc. */
  236.  
  237.   last_hist = i - 2;
  238.  
  239.   if (list)
  240.     {
  241.       histbeg = fc_gethnum (list->word->word, hlist);
  242.       list = list->next;
  243.  
  244.       if (list)
  245.     histend = fc_gethnum (list->word->word, hlist);
  246.       else
  247.     {
  248.       if (listing)
  249.         histend = last_hist;
  250.       else
  251.         histend = histbeg;
  252.     }
  253.     }
  254.   else
  255.     {
  256.       /* The default for listing is the last 16 history items. */
  257.       if (listing)
  258.     {
  259.       histend = last_hist;
  260.       histbeg = histend - 16;
  261.     }
  262.       else
  263.     {
  264.       /* For editing, it is the last history command. */
  265.       histbeg = histend = last_hist;
  266.       }
  267.     }
  268.  
  269.   /* We print error messages for line specifications out of range. */
  270.   if ((histbeg < 0) || (histend < 0) ||
  271.       (histbeg > last_hist) || (histend > last_hist))
  272.     {
  273.       builtin_error ("history specification out of range");
  274.       return (EXECUTION_FAILURE);
  275.     }
  276.  
  277.   if (histend < histbeg)
  278.     {
  279.       int t = histend;
  280.  
  281.       histend = histbeg;
  282.       histbeg = t;
  283.       reverse = 1;
  284.     }
  285.  
  286.   if (listing)
  287.     stream = stdout;
  288.   else
  289.     {
  290.       numbering = 0;
  291.       sprintf (fn, "/tmp/bash%d", (int)time ((long *) 0) + (int)getpid ());
  292.  
  293.       stream = fopen (fn, "w");
  294.  
  295.       if (!stream)
  296.     {
  297.       builtin_error ("cannot open temp file %s", fn);
  298.       return (EXECUTION_FAILURE);
  299.     }
  300.     }
  301.  
  302.   if (!reverse)
  303.     {
  304.       for (i = histbeg; i <= histend; i++)
  305.     {
  306.       QUIT;
  307.       if (numbering)
  308.         fprintf (stream, "%5d%c", i + history_base,
  309.              histdata (i) ? '*' : ' ');
  310.  
  311.       fprintf (stream, "%s\n", histline (i));
  312.     }
  313.     }
  314.   else
  315.     {
  316.       for (i = histend; i >= histbeg; i--)
  317.     {
  318.       QUIT;
  319.       if (numbering)
  320.         fprintf (stream, "%5d%c", i + history_base,
  321.              histdata (i) ? '*' : ' ');
  322.       fprintf (stream, "%s\n", histline (i));
  323.     }
  324.     }
  325.  
  326.   if (listing)
  327.     return (EXECUTION_SUCCESS);
  328.  
  329.   fclose (stream);
  330.  
  331.   /* Now edit the file of commands. */
  332.   if (ename)
  333.     {
  334.       command = (char *)xmalloc (strlen (ename) + strlen (fn) + 2);
  335.       sprintf (command, "%s %s", ename, fn);
  336.     }
  337.   else
  338.     {
  339.       command = (char *)xmalloc (3 + strlen (FC_EDIT_COMMAND) + strlen (fn));
  340.       sprintf (command, "%s %s", FC_EDIT_COMMAND, fn);
  341.     }
  342.   parse_and_execute (command, "fc");
  343.  
  344.   /* Now reopen the file and execute the edited commands. */
  345.  
  346.   stream = fopen (fn, "r");
  347.   unlink (fn);
  348.  
  349.   if (stream == NULL)
  350.     {
  351.       builtin_error ("cannot reopen temp file %s", fn);
  352.       return (EXECUTION_FAILURE);
  353.     }
  354.  
  355.   retval = EXECUTION_SUCCESS;
  356.   first = 1;
  357.  
  358.   while ((line = fc_readline (stream)) != NULL)
  359.     {
  360.       if (line[0] == '\n')
  361.     {
  362.       free (line);
  363.       continue;        /* Skip blank lines. */
  364.     }
  365.  
  366.       printf ("%s", line);    /* LINE already ends with a newline. */
  367.  
  368.       if (first)
  369.     {
  370.       first = 0;
  371.       fc_replhist (line);
  372.     }
  373.       else
  374.     fc_addhist (line);
  375.       retval = parse_and_execute (line, "fc");
  376.     }
  377.   fclose (stream);
  378.   return (retval);
  379. }
  380.  
  381. /* Return an absolute index into HLIST which corresponds to COMMAND.  If
  382.    COMMAND is a number, then it was specified in relative terms.  If it
  383.    is a string, then it is the start of a command line present in HLIST. */
  384. static int
  385. fc_gethnum (command, hlist)
  386.      char *command;
  387.      HIST_ENTRY **hlist;
  388. {
  389.   int sign = 1, n;
  390.   register int i, j;
  391.   register char *s;
  392.  
  393.   /* Count history elements. */
  394.   for (i = 0; hlist[i]; i++);
  395.  
  396.   /* With the Bash implementation of history, the current command line
  397.      ("fc blah..." and so on) is already part of the history list by
  398.      the time we get to this point.  This just skips over that command
  399.      and makes the last command that this deals with be the last command
  400.      the user entered before the fc. */
  401.   i -= 2;
  402.  
  403.   /* No specification defaults to most recent command. */
  404.   if (command == NULL)
  405.     return (i);
  406.  
  407.   /* Otherwise, there is a specification.  It can be a number relative to
  408.      the current position, or an absolute history number. */
  409.   s = command;
  410.  
  411.   /* Handle possible leading minus sign. */
  412.   if (s && (*s == '-'))
  413.     {
  414.       sign = -1;
  415.       s++;
  416.     }
  417.  
  418.   if (s && digit(*s))
  419.     {
  420.       n = atoi (s);
  421.       n *= sign;
  422.  
  423.       /* Anything specified greater than the last history element that we
  424.      deal with is an error. */
  425.       if (n > i + history_base)
  426.     return (-1);
  427.  
  428.       /* If the value is negative or zero, then it is an offset from
  429.      the current history item. */
  430.       if (n <= 0)
  431.     return (i + n);
  432.  
  433.       return (n - history_base);
  434.     }
  435.  
  436.   for (j = i; j >= 0; j--)
  437.     {
  438.       if (prefix (command, histline (j)))
  439.     return (j);
  440.     }
  441.   return (-1);
  442. }
  443.  
  444. /* Locate the most recent history line which begins with
  445.    COMMAND in HLIST, and return a malloc()'ed copy of it. */
  446. static char *
  447. fc_gethist (command, hlist)
  448.      char *command;
  449.      HIST_ENTRY **hlist;
  450. {
  451.   int i;
  452.  
  453.   i = fc_gethnum (command, hlist);
  454.  
  455.   if (i >= 0)
  456.     return (savestring (histline (i)));
  457.   else
  458.     return ((char *)NULL);
  459. }
  460.  
  461. /* Read the edited history lines from STREAM and return them
  462.    one at a time.  This can read unlimited length lines.  The
  463.    caller should free the storage. */
  464. static char *
  465. fc_readline (stream)
  466.      FILE *stream;
  467. {
  468.   register int c;
  469.   int line_len = 0, lindex = 0;
  470.   char *line = (char *)NULL;
  471.  
  472.   while ((c = getc (stream)) != EOF)
  473.     {
  474.       if ((lindex + 2) >= line_len)
  475.     line = (char *) xrealloc (line, (line_len += 128));
  476.  
  477.       if (c == '\n')
  478.     {
  479.       line[lindex++] = '\n';
  480.       line[lindex++] = '\0';
  481.       return (line);
  482.     }
  483.       else
  484.     line[lindex++] = c;
  485.     }
  486.  
  487.   if (!lindex)
  488.     {
  489.       if (line)
  490.     free (line);
  491.  
  492.       return ((char *)NULL);
  493.     }
  494.  
  495.   if (lindex + 2 >= line_len)
  496.     line = (char *)xrealloc (line, lindex + 3);
  497.  
  498.   line[lindex++] = '\n';        /* Finish with newline if none in file */
  499.   line[lindex++] = '\0';
  500.   return (line);
  501. }
  502.  
  503. /* Perform the SUBS on COMMAND.
  504.    SUBS is a list of substitutions, and COMMAND is a simple string.
  505.    Return a pointer to a malloc'ed string which contains the substituted
  506.    command. */
  507. static char *
  508. fc_dosubs (command, subs)
  509.      char *command;
  510.      REPL *subs;
  511. {
  512.   register char *new = savestring (command);
  513.   register REPL *r;
  514.  
  515.   for (r = subs; r; r = r->next)
  516.     {
  517.       register char *t;
  518.  
  519.       t = fc_replace (r->pat, r->rep, new);
  520.       free (new);
  521.       new = t;
  522.     }
  523.   return (new);
  524. }
  525.  
  526. /* Replace the occurrences of PAT with REP in COMMAND.
  527.    This returns a new string; the caller should free it. */
  528. static char *
  529. fc_replace (pat, rep, command)
  530.      char *pat, *rep, *command;
  531. {
  532.   register int i;
  533.   int patlen, replen, templen;
  534.   char *new, *temp;
  535.  
  536.   patlen = strlen (pat);
  537.   replen = strlen (rep);
  538.  
  539.   temp = savestring (command);
  540.   templen = strlen (temp);
  541.   i = 0;
  542.  
  543.   for (; (i + patlen) <= templen; i++)
  544.     {
  545.       if (strncmp (temp + i, pat, patlen) == 0)
  546.     {
  547.       new = (char *) xmalloc (1 + (replen - patlen) + templen);
  548.  
  549.       strncpy (new, temp, i);
  550.       strncpy (new + i, rep, replen);
  551.       strncpy (new + i + replen,
  552.            temp + i + patlen, templen - (i + patlen));
  553.       new[templen + (replen - patlen)] = '\0'; /* just in case */
  554.  
  555.       free (temp);
  556.       temp = new;
  557.       i += replen;
  558.       templen = strlen (temp);
  559.     }
  560.     }
  561.   return (temp);
  562. }
  563.  
  564. /* Use `command' to replace the last entry in the history list, which,
  565.    by this time, is `fc blah...'.  The intent is that the new command
  566.    become the history entry, and that `fc' should never appear in the
  567.    history list.  This way you can do `r' to your heart's content.
  568.  
  569.    Should this do anything with the history_control variable? */
  570. static void
  571. fc_replhist (command)
  572.      char *command;
  573. {
  574.   register int i;
  575.   HIST_ENTRY **hlist, *histent, *discard, *history_get ();
  576.   char *data;
  577.   int n;
  578.  
  579.   if (command == NULL || *command == NULL)
  580.     return;
  581.  
  582.   hlist = history_list ();
  583.  
  584.   if (hlist == NULL)
  585.     return;
  586.  
  587.   for (i = 0; hlist[i]; i++);
  588.   i--;
  589.  
  590.   /* History_get () takes a parameter that should be
  591.      offset by history_base. */
  592.  
  593.   histent = history_get (history_base + i);    /* Don't free this */
  594.   if (histent == NULL)
  595.     return;
  596.  
  597.   if (histent->data)
  598.     data = savestring (histent->data);
  599.   else
  600.     data = (char *) NULL;
  601.  
  602.   n = strlen (command);
  603.  
  604.   if (command[n - 1] == '\n')
  605.     command[n - 1] = '\0';
  606.  
  607.   if (command && *command)
  608.     {
  609.       discard = replace_history_entry (i, command, data);
  610.       if (discard)
  611.     {
  612.       if (discard->line)
  613.         free (discard->line);
  614.  
  615.       free ((char *) discard);
  616.     }
  617.     }
  618. }
  619.  
  620. /* Add LINE to the history, after removing a single trailing newline. */
  621. static void
  622. fc_addhist (line)
  623.      char *line;
  624. {
  625.   register int n = strlen (line);
  626.  
  627.   if (line[n - 1] == '\n')
  628.     line[n - 1] = '\0';
  629.  
  630.   if (line && *line)
  631.     add_history (line);
  632. }
  633.